# 药品管理
import enum
import inspect
import os
import time
from pathlib import Path
from typing import Optional
from typing import Type

from fastapi import APIRouter, UploadFile, File, Form, Depends
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from pydantic.fields import ModelField
from sqlalchemy import Column, Integer, String, DateTime, func, Enum, Float, Text, and_
from sqlalchemy.exc import InvalidRequestError

from setting.database import Base, db_session

medicine_router = APIRouter()
media_path = os.path.join(Path(__file__).resolve().parent.parent, 'media')


class MedicineCategoryEnum(enum.Enum):
    otc = "otc"
    pd = "pd"


def as_form(cls: Type[BaseModel]):
    new_parameters = []
    for field_name, model_field in cls.__fields__.items():
        model_field: ModelField  # type: ignore
        new_parameters.append(
            inspect.Parameter(
                model_field.alias,
                inspect.Parameter.POSITIONAL_ONLY,
                default=Form(...) if model_field.required else Form(model_field.default),
                annotation=model_field.outer_type_,
            )
        )

    async def as_form_func(**data):
        return cls(**data)

    sig = inspect.signature(as_form_func)
    sig = sig.replace(parameters=new_parameters)
    as_form_func.__signature__ = sig  # type: ignore
    setattr(cls, 'as_form', as_form_func)
    return cls


@as_form
class CreateIn(BaseModel):
    name: str
    repertory: int
    approval_number: str
    category: MedicineCategoryEnum = MedicineCategoryEnum.otc
    specification: str
    type: str
    price: float
    image: UploadFile
    manufacturer: str
    desc: Optional[str] = ""

    class Config:
        orm_mode = True


@as_form
class UpdateIn(BaseModel):
    # id: int
    name: str
    repertory: int
    approval_number: str
    category: MedicineCategoryEnum
    specification: str
    type: str
    price: float
    image: Optional[UploadFile]
    manufacturer: str
    desc: Optional[str] = ""

    class Config:
        orm_mode = True


def save_file(data: UploadFile):
    """
        保存文件
    :param data:
    :return:
    """
    if os.path.exists(media_path) is False:  # 没有路径先创建
        os.makedirs(media_path)
    time_ns = str(int(time.time_ns()))
    name, suffix = os.path.splitext(data.filename)
    new_file_name = f'{name}_{time_ns}{suffix}'
    file_path = os.path.join(media_path, new_file_name)
    with open(file_path, 'wb') as f:
        for i in iter(lambda: data.file.read(1024 * 1024 * 10), b''):
            f.write(i)
    f.close()
    image_url = f'/media/{new_file_name}'
    return image_url


class Medicine(Base):
    """
        药品表
    """
    __tablename__ = 'medicine'

    id = Column(Integer, primary_key=True, autoincrement=True, comment='ID')
    name = Column(String(300), comment="药品名称")
    repertory = Column(Integer, comment="库存")
    approval_number = Column(String(200), comment="批准文号")
    specification = Column(String(200), comment="规格")
    category = Column(Enum("otc", "pd", name='category'), default="otc", comment="药品类别")  # 处方和非处方
    type = Column(String(200), comment="药品种类")
    price = Column(Float, comment="价格")
    image = Column(String(500), comment="药品图片")
    manufacturer = Column(String(300), comment="厂家")
    desc = Column(Text, comment="描述")
    create_time = Column(DateTime, server_default=func.now(), comment="创建时间")
    updated_time = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="修改时间")  # 修改时自动更新时间

    def __repr__(self):
        return f'id: {self.id}, name: {self.name}'


@medicine_router.get('/')
def medicine_list(name: str = "",
                  page: int = 1,
                  page_size: int = 10):
    """
    药品列表
    :param name: 药品名称
    :param page: 页码
    :param page_size: 页数
    :return:
    """
    # 参数
    try:
        args = []
        if name:
            args.append(Medicine.name.ilike(f'%{name}%'))
        query = db_session.query(Medicine).filter(and_(*args))  # 将参数查询数据保存到数组中一起执行
        all_medicine = query.offset((page - 1) * page_size).limit(page_size).all()
        medicine_list = [jsonable_encoder(i) for i in all_medicine]
        medicine_total = db_session.query(func.count(Medicine.id)).filter(and_(*args)).scalar()
        return {
            "message": "ok",
            "code": 0,
            "data": {
                "list": medicine_list,
                "total": medicine_total,
            }
        }
    except Exception as e:
        return {
            "message": e.args,
            "code": 500,
        }


@medicine_router.post("/")
def create_medicine(data: CreateIn = Depends(CreateIn.as_form)):
    """
        添加数据
    :param data:
    :return:
    """
    if not data.image:
        return {"message": "图片必传", "code": 400}
    try:
        image = data.image
        image_url = save_file(image)
        data.image = image_url
        json_data = jsonable_encoder(data)
        db_obj = Medicine(**json_data)
        try:
            db_session.add(db_obj)
            db_session.commit()
        except InvalidRequestError:
            db_session.session.rollback()
        return {
            "message": "ok",
            "data": json_data,
            "code": 0
        }
    except Exception as e:
        return {
            "message": e.args,
            "code": 500
        }


@medicine_router.delete("/{pk}")
def remove_medicine(pk: int):
    try:
        db_session.query(Medicine).filter(Medicine.id == pk).delete()
        db_session.commit()
        return {"message": "ok", "code": 0}
    except Exception as e:
        return {"message": e.args, "code": 500}


@medicine_router.get("/{pk}")
def get_medicine(pk: int):
    data = db_session.query(Medicine).filter_by(id=pk).first()
    if data is None:
        return {"message": "数据不存在", "code": 400}
    return {"message": "ok", "code": 0, "data": jsonable_encoder(data)}


@medicine_router.put("/{pk}")
def update_medicine(pk: int, data: UpdateIn = Depends(UpdateIn.as_form)):
    medicine = db_session.query(Medicine).filter_by(id=pk).first()
    if medicine is None:
        return {"message": "数据不存在", "code": 400}
    if data.image.filename:
        medicine.image = save_file(data.image)
    medicine.name = data.name
    medicine.category = data.category.value
    medicine.type = data.type
    medicine.desc = data.desc
    medicine.approval_number = data.approval_number
    medicine.price = data.price
    medicine.repertory = data.repertory
    medicine.specification = data.specification
    db_session.commit()
    db_session.refresh(medicine)
    return {
        "message": "ok",
        "data": jsonable_encoder(medicine),
        "code": 0
    }
